﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Up2Link.Util
{
    /// <summary>
    /// 在命令控制符模式下实现非阻塞接受用户键盘输入并同时显示输出的类
    /// </summary>
    public class CConsole
    {
        /// <summary>
        /// 用于显示输出的线程
        /// </summary>
        private Thread output;

        /// <summary>
        /// 用于接受用户输入的线程
        /// </summary>
        private Thread input;

        /// <summary>
        /// 存储需要输出的信息的队列
        /// </summary>
        private Queue<String> outputStr = new Queue<String>();

        /// <summary>
        /// 存储接受到的用户输入的队列
        /// </summary>
        private Queue<String> inputStr = new Queue<String>();

        /// <summary>
        /// 运行状态标记
        /// </summary>
        private Boolean consoleOn = false;

        /// <summary>
        /// 无参数构造函数
        /// 创建两个线程对象
        /// </summary>
        public CConsole()
        {
            output = new Thread(new ThreadStart(OutputUpdate));
            input = new Thread(new ThreadStart(InputUpdate));
        }

        /// <summary>
        /// 启动命令提示符非阻塞输入输出
        /// </summary>
        public void Start()
        {
            //初始化屏幕
            Console.Clear();

            //初始化光标位置
            Console.CursorLeft = 0;
            Console.CursorTop = 20;
            
            //显示提示信息
            for (Int32 i = 0; i < 80; i++)
            {
                Console.Write('=');
            }
            Console.WriteLine("请输入信息：");

            //修改运行状态标记
            consoleOn = true;

            //启动线程
            output.Start();
            input.Start();
        }

        /// <summary>
        /// 关闭命令提示符非阻塞输入输出
        /// </summary>
        public void Close()
        {
            //修改运行状态标记
            consoleOn = false;

            //等待线程结束
            output.Join();
            input.Join();
        }

        /// <summary>
        /// 将指定的字符串加入输出队列
        /// </summary>
        /// <param name="str">需要输出的字符串</param>
        public void SendOutput(String str)
        {
            //判断字符串是否为空
            if (str != null)
            {
                outputStr.Enqueue(str);
            }
        }

        /// <summary>
        /// 获得用户输入的字符串
        /// </summary>
        /// <returns>返回队列的第一个字符串，如果队列为空则返回null</returns>
        public String ReceiveInput()
        {
            //如果用户输入队列为空，则返回null
            if (inputStr.Count > 0)
            {
                return inputStr.Dequeue();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 显示输入信息的线程
        /// </summary>
        private void OutputUpdate()
        {
            Int32 lineCount = 0;
            String str;

            Int32 left;
            Int32 top;

            //循环条件为运行状态标记为真
            while (consoleOn)
            {
                //如果有输出信息需要显示
                if (outputStr.Count > 0)
                {
                    //如果显示区域已满，则将之前显示的内容全部上移一行
                    if (lineCount > 19)
                    {
                        Console.MoveBufferArea(0, 1, 80, 19, 0, 0);
                        lineCount = 19;
                    }

                    //修改光标位置
                    left = Console.CursorLeft;
                    top = Console.CursorTop;
                    Console.CursorLeft = 0;
                    Console.CursorTop = lineCount;

                    //显示信息
                    str = outputStr.Dequeue();
                    Console.WriteLine(str);
                    lineCount++;

                    //恢复光标位置
                    Console.CursorLeft = left;
                    Console.CursorTop = top;
                }

                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// 接受用户输入的线程
        /// </summary>
        private void InputUpdate()
        {
            String str;

            //循环条件为运行状态标记为真
            while (consoleOn)
            {
                //等待用户输入，此线程会被阻塞
                str = Console.ReadLine();
                
                //将接受到的用户输入加入队列
                inputStr.Enqueue(str);

                //清除用户输入区域
                Console.CursorLeft = 0;
                Console.CursorTop = 22;
                for (Int32 i = 0; i < 80; i++)
                {
                    Console.Write(' ');
                }

                Console.CursorLeft = 0;
                Console.CursorTop = 22;
            }
        }
    }
}
